#!/usr/bin/env python2

import os
import errno

from disk import Disk
from utils import Exp, _dmsg, _dwarn, _derror, _human_readable, _human_unreadable, _exec_shell1, _exec_system, \
        _str2dict, _syserror, _syswarn, _lock_file1, _unlock_file1, _lock_file

from config import Config

class IntelcasManage(object):
    def __init__(self, node):
        self.node = node
        self.config = Config()

        self.disk = Disk()
        self.cache_log = '%s/cache.log' % (self.config.log_path)
        if not os.path.exists(self.cache_log):
            _exec_system('mkdir -p %s' % self.config.log_path)
            os.system("touch %s" % (self.cache_log))

        self.intelcas_path = "/etc/intelcas"
        self.intelcas_conf = "%s/intelcas.conf" % (self.intelcas_path)
        if not os.path.isfile(self.intelcas_conf):
            if not os.path.isdir(self.intelcas_path):
                os.makedirs(self.intelcas_path)
            f = open(self.intelcas_conf, 'aw+')
            f.write("[caches]\n\n[cores]\n")
            f.close()

    def _get_dev_id(self, dev):
        cmd = "casadm --list-caches 2>/dev/null | grep '%s' | awk '{print $2}'" % (dev)
        out, err = _exec_shell1(cmd, p=False)

        dev_id = out.strip()
        if dev_id == "":
            raise Exp(errno.ENOENT, "not find %s in cache !" % (dev))

        return dev_id

    def _get_intelcasdev_by_coredev(self, coredev):
        cmd = "casadm -L 2>/dev/null | grep '%s' | awk '{print $3, $6}'" % coredev
        try:
            out, err = _exec_shell1(cmd, p=False)
        except Exp as e:
            return None
        if len(out) == 0:
            return None
        __list = out.strip().split(' ')
        intelcasdev = __list[1]
        return intelcasdev

    def _start_cachedev(self, cachedev, _cache_mode):
        cache_mode = self._get_simple_cache_mode(_cache_mode)
        if cache_mode == "":
            raise Exp(errno.EINVAL, "bad cache mode:%s, must be writethrough|writeback|writearound|passthrough" % (_cache_mode))

        cmd = "casadm --start-cache --cache-device %s --cache-mode %s --force" % (cachedev, cache_mode)
        _exec_shell1(cmd)
        # add cache device into intelcas.conf
        cacheid = self._get_dev_id(cachedev)
        cmd= "cp %s %s.tmp && sed -i '/caches/a\%d\t%s\tno\t%s' %s.tmp && cp %s.tmp %s" %\
                 (self.intelcas_conf, self.intelcas_conf, int(cacheid), cachedev, cache_mode, self.intelcas_conf, self.intelcas_conf, self.intelcas_conf)
        _exec_shell1(cmd, p=False)

    def _stop_cachedev(self, cachedev):
        cacheid = self._get_dev_id(cachedev)
        coredevs = self.list_coredevs_by_cache(cachedev)

        cmd = "casadm --stop-cache --cache-id %d" % (int(cacheid))
        _exec_shell1(cmd, p=False)
        #delete cache device from intelcas.conf
        cmd= "cp %s %s.tmp && sed -i '/%s/d' %s.tmp" %\
                 (self.intelcas_conf, self.intelcas_conf, cachedev.replace("/dev/", "\/dev\/"), self.intelcas_conf)
        _exec_shell1(cmd, p=False)

        #delete core devices of this cache from intelcas.conf
        for coredev in coredevs:
            cmd = "sed -i '/%s/d' %s.tmp" % (coredev.replace("/dev/", "\/dev\/"), self.intelcas_conf)
            _exec_shell1(cmd, p=False)

        cmd = "cp %s.tmp %s" % (self.intelcas_conf, self.intelcas_conf)
        _exec_shell1(cmd, p=False)

    def _add_coredev_to_cache(self, cachedev, coredev):
        cacheid = self._get_dev_id(cachedev)
        cmd = "casadm --add-core --cache-id %d --core-device %s" % (int(cacheid), coredev)
        _exec_shell1(cmd)
        # add cache device into intelcas.conf
        cmd= "cp %s %s.tmp && sed -i '/cores/a\%d\t%s' %s.tmp && cp %s.tmp %s" %\
                 (self.intelcas_conf, self.intelcas_conf, int(cacheid), coredev, self.intelcas_conf, self.intelcas_conf, self.intelcas_conf)
        _exec_shell1(cmd, p=False)

    def _remove_coredev_from_cache(self, coredev):
        cacheid, cachedev, status, mode = self.get_cacheinfo_by_coredev(coredev)
        coreid = self._get_dev_id(coredev)
        cmd = "casadm --remove-core --cache-id %d --core-id %d" % (int(cacheid), int(coreid))
        _exec_shell1(cmd, p=False)
        #delete device from intelcas.conf
        cmd= "cp %s %s.tmp && sed -i '/%s/d' %s.tmp && cp %s.tmp %s" %\
                 (self.intelcas_conf, self.intelcas_conf, coredev.replace("/dev/", "\/dev\/"), self.intelcas_conf, self.intelcas_conf, self.intelcas_conf)
        _exec_shell1(cmd, p=False)

    def _get_simple_cache_mode(self, cache_mode):
        mode = ""

        if cache_mode == "writethrough":
            mode = "wt"
        elif cache_mode == "writeback":
            mode = "wb"
        elif cache_mode == "writearound":
            mode = "wa"
        elif cache_mode == "none":
            mode = "pt"
        else:
            mode = ""

        return mode

    def _is_available_cachedev(self, cachedev):
        dev_type = self.disk.get_dev_type(cachedev)
        if dev_type == "SSD" or dev_type == "NVMe":
            return True
        else:
            return False

    def get_cacheinfo_by_coredev(self, coredev):
        cmd = "casadm --list-caches 2>/dev/null | grep %s | awk '{print $6}' | cut -c 14- | awk -F'-' '{print $1}'" % (coredev)
        out, err = _exec_shell1(cmd, p=False)
        cacheid = out.strip()

        cmd = "casadm --list-caches 2>/dev/null | grep '^cache   %d' | awk '{print $3,$4,$5}'" % (int(cacheid))
        out, err = _exec_shell1(cmd, p=False)
        cachedev = out.strip().split(' ')[0]
        cache_status = out.strip().split(' ')[1]
        cache_mode = out.strip().split(' ')[2]

        return cacheid, cachedev, cache_status, cache_mode

    def get_cacheinfo_by_cachedev(self, cachedev):
        cmd = "casadm --list-caches 2>/dev/null | grep '%s' | awk '{print $2,$4,$5}'" % (cachedev)
        out, err = _exec_shell1(cmd, p=False)
        cacheid = out.strip().split(' ')[0]
        cache_status = out.strip().split(' ')[1]
        cache_mode = out.strip().split(' ')[2]

        return cacheid, cache_status, cache_mode

    def get_coredev_by_fastdev(self, coredev):
        cmd = "casadm -L 2>/dev/null| grep '%s' | awk '{print $3}'" % (coredev)
        out, err = _exec_shell1(cmd, p=False)
        return out.strip()

    def get_mappingdev_by_coredev(self, coredev):
        cmd = "casadm -L 2>/dev/null | grep '%s' | awk '{print $6}'" % (coredev)
        try:
            out, err = _exec_shell1(cmd, p=False)
        except Exp as e:
            return None

        if len(out) == 0:
            return None
        return out.strip()

    def is_running_cachedev(self, cachedev):
        try:
            cmd = "casadm --list-caches 2>/dev/null | grep '^cache' | grep %s" % (cachedev)
            _exec_shell1(cmd, p=False)
            return True
        except Exp, e:
            return False

    def is_running_coredev(self, coredev):
        try:
            cmd = "casadm --list-caches 2>/dev/null | grep 'core' | grep %s" % (coredev)
            _exec_shell1(cmd, p=False)
            return True
        except Exp, e:
            return False

    def bind_cache(self, cachedev, coredev, cache_mode, force):
        lfile = "/var/run/bind_cache.lock"
        lock = _lock_file(lfile)

        if not self._is_available_cachedev(cachedev):
            raise Exp(errno.EPERM, "%s not an availalbe cache device !" % (cachedev))

        if not self.is_running_cachedev(cachedev):
            self._start_cachedev(cachedev, cache_mode)

        self._add_coredev_to_cache(cachedev, coredev)

    def del_cachedev(self, cachedev, is_flush, is_raise=1):
        #add cmd to /opt/fusionstack/log/cache.log, just remember lock file
        fd = _lock_file1("/var/run/cache_log.lock")
        cmd = "grep 'delcachedev %s' %s || echo 'delcachedev %s %d' >> %s" %\
                  (cachedev, self.cache_log, cachedev, is_flush, self.cache_log)
        _exec_shell1(cmd, p=False)
        _unlock_file1(fd)

        #fork
        pid = os.fork()
        if pid == 0:
            try:
                if is_flush:
                    self.flush_cachedev(cachedev)

                self._stop_cachedev(cachedev)
            except Exp, e:
                if is_raise:
                    raise Exp(e.errno, "del %s fail, %s" % (cachedev, e.err))
                else:
                    pass

            # delete from /opt/fusionstack/log/cache.log
            fd = _lock_file1("/var/run/cache_log.lock")
            cmd = "sed -i '/delcachedev %s/d' %s" % (cachedev, self.cache_log)
            _exec_shell1(cmd, p=False)
            _unlock_file1(fd)
        elif pid > 0:
            return
        else:
            raise Exp(errno.EPERM, "fork fail !")

    def del_coredev(self, coredev, is_flush, is_raise=1):
        #add cmd to /opt/fusionstack/log/cache.log, just remember lock file
        fd = _lock_file1("/var/run/cache_log.lock")
        cmd = "grep 'delcoredev %s' %s || echo 'delcoredev %s %d' >> %s" %\
                  (coredev, self.cache_log, coredev, is_flush, self.cache_log)
        _exec_shell1(cmd, p=False)
        _unlock_file1(fd)

        #fork
        pid = os.fork()
        if pid == 0:
            try:
                if is_flush:
                    self.flush_coredev(coredev)

                self._remove_coredev_from_cache(coredev)
            except Exp, e:
                if is_raise:
                    raise Exp(e.errno, "del %s fail, %s" % (coredev, e.err))
                else:
                    pass

            # delete from /opt/fusionstack/log/cache.log
            fd = _lock_file1("/var/run/cache_log.lock")
            cmd = "sed -i '/delcoredev %s/d' %s" % (coredev.replace("/dev/", "\/dev\/"), self.cache_log)
            _exec_shell1(cmd, p=False)
            _unlock_file1(fd)
        elif pid > 0:
            return
        else:
            raise Exp(errno.EPERM, "fork fail !")

    def list_coredevs_by_cache(self, cachedev):
        cacheid = self._get_dev_id(cachedev)
        cmd = "casadm --list-caches 2>/dev/null | grep 'intelcas%d-' | awk '{print $3}'" % (int(cacheid))
        out, err = _exec_shell1(cmd, p=False)

        return out.strip().split('\n')

    def set_cache_mode(self, cachedev, _cache_mode, is_flush):
        cacheid = self._get_dev_id(cachedev)
        if is_flush:
            flush_cache = "yes"
        else:
            flush_cache = "no"

        cache_mode = self._get_simple_cache_mode(_cache_mode)
        if cache_mode == "":
            raise Exp(errno.EINVAL, "bad cache mode:%s, must be writethrough|writeback|writearound|none" % (_cache_mode))

        cmd = "casadm --set-cache-mode --cache-mode %s --cache-id %d --flush-cache %s" % (cache_mode, int(cacheid), flush_cache)
        _exec_shell1(cmd)

    def flush_cachedev(self, cachedev):
        lfile = "/var/run/flush_cache_%s.lock" % (cachedev[5:])
        lock = _lock_file(lfile)

        cacheid = self._get_dev_id(cachedev)
        cmd = "casadm --flush-cache --cache-id %d" % (int(cacheid))
        _exec_shell1(cmd, p=False)

    def flush_coredev(self, coredev):
        lfile = "/var/run/flush_core_%s.lock" % (coredev[5:])
        lock = _lock_file(lfile)

        cacheid, cachedev, status, mode = self.get_cacheinfo_by_coredev(coredev)
        coreid = self._get_dev_id(coredev)

        cmd = "casadm --flush-core --cache-id %d --core-id %d" % (int(cacheid), coreid)
        _exec_shell1(cmd, p=False)
